package com.lgCore.netty.client.handler;

import com.alibaba.fastjson.JSON;
import com.lgCore.client.factory.CommonRpcClientFactory;
import com.lgCore.client.workHandler.ClientNettyBootStrapPool;
import com.lgCore.client.workHandler.ClientResultQueue;
import com.lgCore.message.CommonRpcRequest;
import com.lgCore.netty.client.NettyClientBootstrap;
import com.lgCore.netty.message.*;
import com.lgCore.netty.server.handler.NettyServerHandler;
import com.lgCore.util.ExceptionUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCountUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.SocketAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class NettyClientHandler extends SimpleChannelInboundHandler<BaseMsg> {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettyClientHandler.class);


    private ThreadPoolExecutor threadPoolExecutor;

    public NettyClientHandler(int threadCount) {
        threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadCount);
    }

    //利用写空闲发送心跳检测消息
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            Attribute<Constants> constantsAttributeKey = ctx.channel().attr(ClientNettyBootStrapPool.auth);
            Constants constants = constantsAttributeKey.get();
            IdleStateEvent e = (IdleStateEvent) evt;
            switch (e.state()) {
                case WRITER_IDLE:
                    PingMsg pingMsg = new PingMsg();
                    if (constants != null) {
                        pingMsg.setClientId(constants.getClientId());
                        ctx.writeAndFlush(pingMsg);
//                        LOGGER.info("客户端：发送ping指令" + JSON.toJSONString(pingMsg));
                    }
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //这里执行客户端断开连接后的操作
        String ipAddr = ctx.channel().remoteAddress().toString();
        ipAddr = ipAddr.substring(1, ipAddr.length());
        //删除netty连接
        CommonRpcClientFactory.getiCommonRpcClientService().removeServerBootStrap(ipAddr);
        LOGGER.info("客户端：与服务端断开连接" + ipAddr);

    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, BaseMsg baseMsg) throws Exception {
//        System.out.println("client msg is "+JSON.toJSONString(baseMsg)+" 时间 "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        Attribute<Constants> constantsAttributeKey = channelHandlerContext.channel().attr(ClientNettyBootStrapPool.auth);
        Constants constants = constantsAttributeKey.get();
        MsgType msgType = baseMsg.getType();
        switch (msgType) {
            case LOGIN: {
                //向服务器发起登录
                LoginMsg loginMsg = new LoginMsg();
//                Attribute<String> ipAddrKeyAttributeKey= channelHandlerContext.attr(ipAddrKey);
//                ipAddrKeyAttributeKey.set(loginMsg.getIpAddrKey());
                channelHandlerContext.writeAndFlush(loginMsg);
            }
            break;
            case PING: {
                //接受服务端心跳ping信息
//                System.out.println("receive ping from server----------" + new Date());
            }
            break;
            case ASK: {
                //发送业务数据
                ReplyClientBody replyClientBody = new ReplyClientBody("client info **** !!!");
                ReplyMsg replyMsg = new ReplyMsg();
                replyMsg.setClientId(constants.getClientId());
                replyMsg.setBody(replyClientBody);
                channelHandlerContext.writeAndFlush(replyMsg);
            }
            break;
            case REPLY: {
                //接受服务端数据
                ReplyMsg replyMsg = (ReplyMsg) baseMsg;
//                LOGGER.info("receive server msg: " + JSON.toJSONString(replyMsg));
                //业务处理
                threadPoolExecutor.submit(new ClientHandlerRunnable(replyMsg));
            }
            break;
            case LOGIN_REPLY: {
                //收到服务端登录结果
                //ReplyClientBody replyClientBody=new ReplyClientBody("client info **** !!!");
                LoginReplyMsg loginReplyMsg = (LoginReplyMsg) baseMsg;
                constantsAttributeKey.set(new Constants(loginReplyMsg.getLoginToken()));
//                ClientNettyBootStrapPool.putConstant();
            }
            break;
            default:
                break;
        }
        ReferenceCountUtil.release(msgType);
    }


    /**
     * 捕捉异常，让netty正常运行
     *
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable e)
            throws Exception {
        String errorMsg = ExceptionUtil.ExceptionDetailAll((Exception) e.getCause());
        LOGGER.info("netty server exception is " + errorMsg);
        if (!(e.getCause() instanceof IOException)) {
            // only log
            LOGGER.error("catch some exception not IOException", e);
        }
    }

    private class ClientHandlerRunnable implements Runnable {

        private ReplyMsg replyMsg;

        /**
         * @param replyMsg 客户端业务数据
         */
        public ClientHandlerRunnable(ReplyMsg replyMsg) {
            super();
            this.replyMsg = replyMsg;
        }


        @Override
        public void run() {
            try {
                ClientResultQueue.receiveResponse(replyMsg);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

}
